package com.gitee.wsl.android.security

import android.annotation.SuppressLint
import android.content.pm.Signature
import com.gitee.wsl.jvm.ext.base.DateTimeExt.sampleDateTimeFormat
import timber.log.Timber
import java.io.ByteArrayInputStream
import java.io.File
import java.security.MessageDigest
import java.security.cert.Certificate
import java.security.cert.CertificateEncodingException
import java.security.cert.CertificateExpiredException
import java.security.cert.CertificateFactory
import java.security.cert.CertificateNotYetValidException
import java.security.cert.X509Certificate
import java.text.SimpleDateFormat
import java.util.jar.JarEntry
import java.util.jar.JarFile
import javax.security.auth.x500.X500Principal
import java.lang.StringBuilder

typealias SignatureCall = (List<Signature>)->String


object Signatures{
    val MD5: SignatureCall = SignatureExt::signatureMD5

    val SHA1: SignatureCall = SignatureExt::signatureSHA1
}

/**
 * detail: 签名工具类 ( 获取 APP 签名信息 )
 * @author Ttt
 * <pre>
 *     Android 的 APK 应用签名机制以及读取签名的方法
 *     @see <a href="http://www.jb51.net/article/79894.htm"/>
 * </pre>
 */
object SignatureExt {

    /**
     * 判断签名是 debug 签名还是 release 签名
     * 检测应用程序是否是用 "CN=Android Debug,O=Android,C=US" 的 debug 信息进行签名
     */
    private val DEBUG_DN = X500Principal("CN=Android Debug,O=Android,C=US")

    /**
     * 获取 MD5 签名
     * @param signatures [Signature][]
     * @return MD5 签名
     */
    @OptIn(ExperimentalStdlibApi::class)
    fun signatureMD5(signatures: List<Signature>): String {
        return try {
            val digest = MessageDigest.getInstance("MD5")
            for (sign in signatures) {
                digest.update(sign.toByteArray())
             }
            digest.digest().toHexString()
        } catch (e: Exception) {
            Timber.d( e, "signatureMD5")
            ""
        }
    }

    /**
     * 获取签名 SHA1 加密字符串
     * @param signatures [Signature][]
     * @return 签名 SHA1 加密字符串
     */
    @OptIn(ExperimentalStdlibApi::class)
    fun signatureSHA1(signatures: List<Signature>): String {
        return try {
            val digest = MessageDigest.getInstance("SHA-1")

                for (sign in signatures) {
                   digest.update(sign.toByteArray())
                }

            digest.digest().toHexString()
        } catch (e: Exception) {
            Timber.d( e, "signatureSHA1")
            ""
        }
    }

    /**
     * 获取签名 SHA256 加密字符串
     * @param signatures [Signature][]
     * @return 签名 SHA256 加密字符串
     */
    @OptIn(ExperimentalStdlibApi::class)
    fun signatureSHA256(signatures: List<Signature>): String {
        return try {
            val digest = MessageDigest.getInstance("SHA-256")
                for (sign in signatures) {
                    digest.update(sign.toByteArray())
                }
            digest.digest().toHexString()
        } catch (e: Exception) {
            Timber.d( e, "signatureSHA256")
            ""
        }
    }

    /**
     * 判断 debug 签名还是 release 签名
     * @param signatures [Signature][]
     * @return `true` debug.keystore, `false` release.keystore
     */
    @SuppressLint("SuspiciousIndentation")
    fun isDebuggable(signatures: List<Signature>): Boolean {
        // 默认属于 debug 签名
        var debuggable = true
            try {
                for (sign in signatures) {
                        val cert = getX509Certificate(sign)
                        if (cert != null && cert.subjectX500Principal != null) {
                            debuggable = DEBUG_DN == cert.subjectX500Principal
                            if (debuggable) {
                                break
                            }
                        }
                }
            } catch (e: Exception) {
                Timber.d( e, "isDebuggable")
            }
        return debuggable
    }

    /**
     * 获取证书对象
     * @param signatures [Signature][]
     * @return [X509Certificate]
     */
    fun getX509Certificate(signatures: List<Signature>): X509Certificate? {
        return if (signatures.isNotEmpty()) {
            getX509Certificate(signatures[0])
        } else null
    }

    /**
     * 获取证书对象
     * @param signature [Signature]
     * @return [X509Certificate]
     */
    fun getX509Certificate(signature: Signature): X509Certificate? {
        try {
           val cf = CertificateFactory.getInstance("X.509")
           val bais = ByteArrayInputStream(signature.toByteArray())
           return cf.generateCertificate(bais) as X509Certificate
         } catch (e: Exception) {
            Timber.d( e, "getX509Certificate")
         }
        return null
    }

    /**
     * 打印签名信息
     * @param signatures [Signature][]
     * @return 签名信息 [List]
     */
    @OptIn(ExperimentalStdlibApi::class)
    fun printSignatureInfo(signatures: List<Signature>): List<Map<String, String>> {
        val lists: MutableList<Map<String, String>> = ArrayList()
        try {
            // 格式化日期
            val sdf: SimpleDateFormat = sampleDateTimeFormat
            // 遍历获取
            for (sign in signatures) {

                    val cert = getX509Certificate(sign)

                    // 证书生成日期
                    val notBefore = cert!!.notBefore
                    // 证书有效期
                    val notAfter = cert.notAfter
                    // 设置有效期
                    val builder = StringBuilder()
                    builder.append(sdf.format(notBefore))
                    builder.append(" to ") // 至
                    builder.append(sdf.format(notAfter))
                    builder.append(com.gitee.wsl.ext.K.SYMBOL.NEW_LINE_X2)
                    builder.append(notBefore)
                    builder.append(" to ")
                    builder.append(notAfter)
                    // 保存有效期转换信息
                    val effectiveStr = builder.toString()
                    // 证书是否过期
                    var effective = false
                    try {
                        cert.checkValidity()
                        // CertificateExpiredException ( 证书已过期 )
                        // CertificateNotYetValidException ( 证书不再有效 )
                    } catch (ce: CertificateExpiredException) {
                        effective = true
                    } catch (ce: CertificateNotYetValidException) {
                        effective = true
                    }
                    // 证书发布方
                    val certPrincipal = cert.issuerX500Principal.toString()
                    // 证书版本号
                    val certVersion = cert.version.toString()
                    // 证书算法名称
                    val certSigAlgName = cert.sigAlgName
                    // 证书算法 OID
                    val certSigAlgOID = cert.sigAlgOID
                    // 证书机器码
                    val certSerialnumber = cert.serialNumber.toString()
                    // 证书 DER 编码
                    var certDERCode: String = ""
                    try {
                        // 证书 DER 编码
                        certDERCode = cert.tbsCertificate.toHexString()
                    } catch (ignored: CertificateEncodingException) {
                        Timber.d(ignored)
                    }
                    // 证书公钥
                    val pubKey = cert.publicKey.toString()

                    // 保存信息
                    val maps: MutableMap<String, String> = LinkedHashMap()
                    maps["证书有效期信息"] = effectiveStr
                    maps["证书是否过期"] = java.lang.Boolean.toString(effective)
                    maps["证书发布方"] = certPrincipal
                    maps["证书版本号"] = certVersion
                    maps["证书算法名称"] = certSigAlgName
                    maps["证书算法 OID"] = certSigAlgOID
                    maps["证书机器码"] = certSerialnumber
                    maps["证书 DER 编码"] = certDERCode
                    maps["证书公钥"] = pubKey
                    lists.add(maps)
                }
        } catch (e: Exception) {
            Timber.d( e, "printSignatureInfo")
        }
        return lists
    }

    // =
    /**
     * 从 APK 中读取签名
     * @param file 文件
     * @return [Signature][]
     */
    fun getSignaturesFromApk(file: File): Array<Signature>? {
        val certificates = getCertificateFromApk(file) ?: return null
        try {
            return arrayOf(Signature(certificates[0].encoded))
        } catch (e: Exception) {
            Timber.d( e, "getSignaturesFromApk")
        }
        return null
    }

    /**
     * 从 APK 中读取签名
     * @param file 文件
     * @return [Certificate][]
     */
    fun getCertificateFromApk(file: File): Array<Certificate>? {
        try {
            val jarFile = JarFile(file)
            val je = jarFile.getJarEntry("AndroidManifest.xml")
            val readBuffer = ByteArray(8192)
            return loadCertificates(jarFile, je, readBuffer)
        } catch (e: Exception) {
            Timber.d( e, "getCertificateFromApk")
        }
        return null
    }

    /**
     * 加载文件, 获取签名信息
     * @param jarFile    [JarFile]
     * @param jarEntry   [JarEntry]
     * @param readBuffer 文件 Buffer
     * @return [Certificate][]
     */
    private fun loadCertificates(
        jarFile: JarFile,
        jarEntry: JarEntry,
        readBuffer: ByteArray
    ): Array<Certificate>? {
        try {
            val `is` = jarFile.getInputStream(jarEntry)
            while (`is`.read(readBuffer, 0, readBuffer.size) != -1) { }
            `is`.close()
            return jarEntry.certificates
        } catch (e: Exception) {
            Timber.d( e, "loadCertificates")
        }
        return null
    }
}